/** @
 *  @FileName     : cfg.c
 *  @Author       : Chase Wu
 *  @Date         : 2021-05-28 16:51:51
 *  @LastEditTime : 2021-06-10 11:12:48
 */

#include "cfg.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdbool.h>

#include "log.h"

static const char *bool_str[] = {"false", "true"};
static const char *video_mode[] = {"sdi_4x1", "sdi_1x4"};
static const char *encoder_mode[] = {"h264", "h265"};
static const char *profile_mode[] = {"avc_baseline", "avc_main", "avc_high", \
                              "avc_high10", "avc_highI10", "avc_high422", \
                              "avc_highI422","hevc_main", "hevc_mainI",  \
                              "hevc_main10", "hevc_mainI10", "hevc_main422",  \
                              "hevc_mainI422"};
static const char *entropy_mode[] = {"cavlc", "cabac"};
static const char *rc_mode[] = {"const_qp", "cbr", "vbr", "low_latency"};
static const char *gop_mode[] = {"default", "pyramidal", "adaptive"};
static const char *interlace_mode[] = {"discard", "interleaved"};
static const char *mosaic_mode[] = {"4x4", "8x8", "16x16", "32x32", "64x64"};
static const char *pixel_format[] = {"y_uv8_420", "y_uv8", "y_uv10_420", "y_uv10"};

static config_info_t g_config = {
    .max_channel = 1,
    .video_mode = VIDEO_1X2160P,
    .vcfg[0] = {
        .channel = 0,
        .nbuffer = 8,
        .encode_config = {
            .enable = true,
            .encoder = ENCODER_H265,
            .width = 3840,
            .height = 2168,
            .bitdepth = 8,
            .uLevel = 51,
            .profile = TY_HEVC_MAIN,
            .entropymode = 1,
            .numslices = 1,

            .rc = {
                .mode = 2,
                .cbpsize = 90000,
                .framerate = 60,
                .targetbitrate = 4000000,
                .maxbitrate = 4000000,
                .initialqp = 27,
                .minqp = 10,
                .maxqp = 52,
                .ipdelta = 12,
                .pbdelta = 32,
            },

            .gop = {
                .mode = 2,
                .length = 100,
                .numb = 2,
                .freqidr = 100,
            },
        },

        .input_config = {
            .width = 3840,
            .height = 2160,
            .fps = 60,
            .interlace = INTERLACE_DISCARD,
        },

        .output_config = {
            .width = 3840,
            .height = 2160,
            .fps = 60,
            .enable = true,
        },

        .scaler_config = {
            .in = {
                .width = 3840,
                .height = 2160,
                .pixfmt = TY_422_8BITS,
            },
            .out = {
                .width = 1920,
                .height = 1080,
                .pixfmt = TY_422_8BITS,
            },
        },

        .mosaic_config = {
            .mode = TY_MOSAIC_4X4,
            .posx = 0,
            .posy = 0,
            .width = 128,
            .height = 64,
        },
    },
    .dcfg[0] = {
        .width = 3840,
        .height = 2160,
        .path = "test.yuv",
    },
};

void show_global_config(cfg_t *config)
{
    int i;

    DBG(DBG_INFO, "max_channel: %d\n", config->info->max_channel);

    for(i = 0; i < config->info->max_channel; i++){
        video_config_t *vcfg;

        vcfg = &config->info->vcfg[i];
        
        DBG(DBG_INFO, "channel: %d\n", i);
        DBG(DBG_INFO, "video buffer num: %d\n",  vcfg->nbuffer);
        DBG(DBG_INFO, "video mode: %s\n",        video_mode[config->info->video_mode]);

        /*video input parameter*/
        DBG(DBG_INFO, "video in width: %d\n",    vcfg->input_config.width);
        DBG(DBG_INFO, "video in height: %d\n",   vcfg->input_config.height);
        DBG(DBG_INFO, "fps: %d\n",               vcfg->input_config.fps);
        DBG(DBG_INFO, "interlace: %s\n",         interlace_mode[vcfg->input_config.interlace]);

        /*video output parameter*/
        DBG(DBG_INFO, "video out width: %d\n",   vcfg->output_config.width);
        DBG(DBG_INFO, "video out height: %d\n",  vcfg->output_config.height);
        DBG(DBG_INFO, "video out fps: %d\n",     vcfg->output_config.fps);
        DBG(DBG_INFO, "video out enable: %s\n",  bool_str[vcfg->output_config.enable]);

        /*video scaler parameter*/
        DBG(DBG_INFO, "scaler in width: %d\n",   vcfg->scaler_config.in.width);
        DBG(DBG_INFO, "scaler in height: %d\n",  vcfg->scaler_config.in.height);
        DBG(DBG_INFO, "scaler in pixfmt: %s\n",  pixel_format[vcfg->scaler_config.in.pixfmt]);
        DBG(DBG_INFO, "scaler out width: %d\n",  vcfg->scaler_config.out.width);
        DBG(DBG_INFO, "scaler out height: %d\n", vcfg->scaler_config.out.height);
        DBG(DBG_INFO, "scaler out pixfmt: %s\n", pixel_format[vcfg->scaler_config.out.pixfmt]);

        /*video mosaic parameter*/
        DBG(DBG_INFO, "mosaic mode: %s\n",       mosaic_mode[vcfg->mosaic_config.mode]);
        DBG(DBG_INFO, "mosaic posx: %d\n",       vcfg->mosaic_config.posx);
        DBG(DBG_INFO, "mosaic posy: %d\n",       vcfg->mosaic_config.posy);
        DBG(DBG_INFO, "mosaic width: %d\n",      vcfg->mosaic_config.width);
        DBG(DBG_INFO, "mosaic height: %d\n",     vcfg->mosaic_config.height);

        /*encode parameter*/
        DBG(DBG_INFO, "encoder enable: %s\n",    bool_str[vcfg->encode_config.enable]);
        DBG(DBG_INFO, "encoder type: %s\n",      encoder_mode[vcfg->encode_config.encoder]);
        DBG(DBG_INFO, "encoder width: %d\n",     vcfg->encode_config.width);
        DBG(DBG_INFO, "encoder height: %d\n",    vcfg->encode_config.height);
        DBG(DBG_INFO, "encoder bitdepth: %d\n",  vcfg->encode_config.bitdepth);
        DBG(DBG_INFO, "encoder Level: %d\n",     vcfg->encode_config.uLevel);
        DBG(DBG_INFO, "encoder profile: %s\n",   profile_mode[vcfg->encode_config.profile]);
        DBG(DBG_INFO, "encoder entropy: %s\n",   entropy_mode[vcfg->encode_config.entropymode]);
        DBG(DBG_INFO, "encoder numslices: %d\n", vcfg->encode_config.numslices);
        DBG(DBG_INFO, "rc mode: %s\n",           rc_mode[vcfg->encode_config.rc.mode]);
        DBG(DBG_INFO, "rc cbpsize: %d\n",        vcfg->encode_config.rc.cbpsize);
        DBG(DBG_INFO, "rc framerate: %d\n",      vcfg->encode_config.rc.framerate);
        DBG(DBG_INFO, "rc target bitrate: %d\n", vcfg->encode_config.rc.targetbitrate);
        DBG(DBG_INFO, "rc maxbitrate: %d\n",     vcfg->encode_config.rc.maxbitrate);
        DBG(DBG_INFO, "rc initialqp: %d\n",      vcfg->encode_config.rc.initialqp);
        DBG(DBG_INFO, "rc minqp: %d\n",          vcfg->encode_config.rc.minqp);
        DBG(DBG_INFO, "rc maxqp: %d\n",          vcfg->encode_config.rc.maxqp);
        DBG(DBG_INFO, "rc pbdelta: %d\n",        vcfg->encode_config.rc.ipdelta);
        DBG(DBG_INFO, "rc pbdelta: %d\n",        vcfg->encode_config.rc.pbdelta);
        DBG(DBG_INFO, "gop mode: %s\n",          gop_mode[vcfg->encode_config.gop.mode]);
        DBG(DBG_INFO, "gop length: %d\n",        vcfg->encode_config.gop.length);
        DBG(DBG_INFO, "gop numb: %d\n",          vcfg->encode_config.gop.numb);
        DBG(DBG_INFO, "gop freqidr: %d\n",       vcfg->encode_config.gop.freqidr);
    }
}

static cJSON *cfg_json_create(void)
{
    cJSON *root, *node;

    root = cJSON_CreateObject();
    if (!root) {
        DBG(DBG_INFO, "Error before: [%s]\n",cJSON_GetErrorPtr());

        return NULL;
    }
    node = cJSON_GetObjectItem(root, "version");
    if(!node){
        node = cJSON_CreateObject();
    }
    if(node){
        CJSON_SET_STRING(root, version,    "1.1");
    }

    return root;
}

static int32_t cfg_json_save(cfg_t *config, cJSON *root)
{
    char *buf;
    int cfg_size = 0;

    int fd;
    char *buffer;
    int size = 0x80000;

    buffer = malloc(size);
    if(!buffer){
        return -ENOMEM;
    }

    buf = cJSON_Print(root);
    if(buf){
        cfg_size = strlen(buf);
        memcpy(buffer, buf, cfg_size);
        free(buf);
    }
        
    cJSON_Delete(root);

    if(config->default_config_file){
        fd = open(config->default_config_file, O_CREAT | O_RDWR | O_TRUNC, 0655);
    }else{
        fd = open("/software/config.json", O_CREAT | O_RDWR | O_TRUNC, 0655);
    }
    if(fd > 0){
        write(fd, buffer, cfg_size);
        close(fd);
    }

    free(buffer);
    buffer = NULL;

    return 0;
}

static void cfg_load_rc_param(cJSON *root, encoder_config_t *encoder)
{
    cJSON *node;

    node = cJSON_GetObjectItem(root, "rc");
    if(!node)
        return;

    CJSON_LOAD_STRING_TO_VALUE(node, mode, encoder->rc.mode, rc_mode);
    CJSON_LOAD_VALUE(node, cbpsize,        encoder->rc.cbpsize);
    CJSON_LOAD_VALUE(node, framerate,      encoder->rc.framerate);
    CJSON_LOAD_VALUE(node, targetbitrate,  encoder->rc.targetbitrate);
    CJSON_LOAD_VALUE(node, maxbitrate,     encoder->rc.maxbitrate);
    CJSON_LOAD_VALUE(node, initialqp,      encoder->rc.initialqp);
    CJSON_LOAD_VALUE(node, minqp,          encoder->rc.minqp);
    CJSON_LOAD_VALUE(node, maxqp,          encoder->rc.maxqp);
    CJSON_LOAD_VALUE(node, ipdelta,        encoder->rc.ipdelta);
    CJSON_LOAD_VALUE(node, pbdelta,        encoder->rc.pbdelta);
}

static void cfg_set_rc_param(cJSON *root, encoder_config_t *encoder)
{
    cJSON *node;

    CJSON_CREATE_NODE(root, "rc", node);
    if(!node)
        return;

    CJSON_SET_STRING(node, mode,          rc_mode[encoder->rc.mode]);
    CJSON_SET_VALUE(node, cbpsize,        encoder->rc.cbpsize);
    CJSON_SET_VALUE(node, framerate,      encoder->rc.framerate);
    CJSON_SET_VALUE(node, targetbitrate,  encoder->rc.targetbitrate);
    CJSON_SET_VALUE(node, maxbitrate,     encoder->rc.maxbitrate);
    CJSON_SET_VALUE(node, initialqp,      encoder->rc.initialqp);
    CJSON_SET_VALUE(node, minqp,          encoder->rc.minqp);
    CJSON_SET_VALUE(node, maxqp,          encoder->rc.maxqp);
    CJSON_SET_VALUE(node, ipdelta,        encoder->rc.ipdelta);
    CJSON_SET_VALUE(node, pbdelta,        encoder->rc.pbdelta);

}

static void cfg_load_gop_param(cJSON *root, encoder_config_t *encoder)
{
    cJSON *node;

    node = cJSON_GetObjectItem(root, "gop");
    if(!node)
        return;

    CJSON_LOAD_STRING_TO_VALUE(node, mode, encoder->gop.mode, gop_mode);
    CJSON_LOAD_VALUE(node, length,         encoder->gop.length);
    CJSON_LOAD_VALUE(node, numb,           encoder->gop.numb);
    CJSON_LOAD_VALUE(node, freqidr,        encoder->gop.freqidr);
}

static void cfg_set_gop_param(cJSON *root, encoder_config_t *encoder)
{
    cJSON *node;

    CJSON_CREATE_NODE(root, "gop", node);
    if(!node)
        return;

    CJSON_SET_STRING(node, mode,    gop_mode[encoder->gop.mode]);
    CJSON_SET_VALUE(node, length,   encoder->gop.length);
    CJSON_SET_VALUE(node, numb,     encoder->gop.numb);
    CJSON_SET_VALUE(node, freqidr,  encoder->gop.freqidr);
}

static void cfg_load_encoder_param(cJSON *root, encoder_config_t *encoder)
{
    CJSON_LOAD_STRING_TO_VALUE(root, enable,      encoder->enable, bool_str);
    CJSON_LOAD_STRING_TO_VALUE(root, encoder,     encoder->encoder, encoder_mode);
    CJSON_LOAD_VALUE(root, width,                 encoder->width);
    CJSON_LOAD_VALUE(root, height,                encoder->height);
    CJSON_LOAD_VALUE(root, bitdepth,              encoder->bitdepth);
    CJSON_LOAD_VALUE(root, level,                 encoder->uLevel);
    CJSON_LOAD_STRING_TO_VALUE(root, profile,     encoder->profile, profile_mode);
    CJSON_LOAD_STRING_TO_VALUE(root, entropymode, encoder->entropymode, entropy_mode);
    CJSON_LOAD_VALUE(root, numslices,             encoder->numslices);

    /*rc param load*/
    cfg_load_rc_param(root, encoder);

    /*gop param load*/
    cfg_load_gop_param(root, encoder);
}

static void cfg_set_encoder_param(cJSON *root, encoder_config_t *encoder)
{
    CJSON_SET_STRING(root, enable,      bool_str[encoder->enable]);
    CJSON_SET_STRING(root, encoder,     encoder_mode[encoder->encoder]);
    CJSON_SET_VALUE(root, width,        encoder->width);
    CJSON_SET_VALUE(root, height,       encoder->height);
    CJSON_SET_VALUE(root, bitdepth,     encoder->bitdepth);
    CJSON_SET_VALUE(root, level,        encoder->uLevel);
    CJSON_SET_STRING(root, profile,     profile_mode[encoder->profile]);
    CJSON_SET_STRING(root, entropymode, entropy_mode[encoder->entropymode]);
    CJSON_SET_VALUE(root, numslices,    encoder->numslices);

    /*rc param set*/
    cfg_set_rc_param(root, encoder);
    /*gop param set*/
    cfg_set_gop_param(root, encoder);
}

static void cfg_load_video_in_param(cJSON *root, input_config_t *input)
{
    CJSON_LOAD_VALUE(root, width,               input->width);
    CJSON_LOAD_VALUE(root, height,              input->height);
    CJSON_LOAD_VALUE(root, fps,                 input->fps);
    CJSON_LOAD_STRING_TO_VALUE(root, interlace, input->interlace, interlace_mode);
}

static void cfg_set_video_in_param(cJSON *root, input_config_t *input)
{
    CJSON_SET_VALUE(root, width,      input->width);
    CJSON_SET_VALUE(root, height,     input->height);
    CJSON_SET_VALUE(root, fps,        input->fps);
    CJSON_SET_STRING(root, interlace, interlace_mode[input->interlace]);
}

static void cfg_load_video_out_param(cJSON *root, output_config_t *output)
{
    CJSON_LOAD_VALUE(root, width,            output->width);
    CJSON_LOAD_VALUE(root, height,           output->height);
    CJSON_LOAD_VALUE(root, fps,              output->fps);
    CJSON_LOAD_STRING_TO_VALUE(root, enable, output->enable, bool_str);
}

static void cfg_set_video_out_param(cJSON *root, output_config_t *output)
{
    CJSON_SET_VALUE(root, width,   output->width);
    CJSON_SET_VALUE(root, height,  output->height);
    CJSON_SET_VALUE(root, fps,     output->fps);
    CJSON_SET_STRING(root, enable, bool_str[output->enable]);
}

static void cfg_load_scaler_param(cJSON *root, scaler_config_t *scaler)
{
    cJSON *node;

    node = cJSON_GetObjectItem(root, "input");
    if(!node)
        return;

    CJSON_LOAD_VALUE(node, width,                  scaler->in.width);
    CJSON_LOAD_VALUE(node, height,                 scaler->in.height);
    CJSON_LOAD_STRING_TO_VALUE(node, pixel format, scaler->in.pixfmt, pixel_format);

    node = cJSON_GetObjectItem(root, "output");
    if(!node)
        return;

    CJSON_LOAD_VALUE(node, width,                  scaler->out.width);
    CJSON_LOAD_VALUE(node, height,                 scaler->out.height);
    CJSON_LOAD_STRING_TO_VALUE(node, pixel format, scaler->out.pixfmt, pixel_format);
}

static void cfg_set_scaler_param(cJSON *root, scaler_config_t *scaler)
{
    cJSON *node;

    CJSON_CREATE_NODE(root, "input", node);
    if(!node)
        return;

    CJSON_SET_VALUE(node, width,         scaler->in.width);
    CJSON_SET_VALUE(node, height,        scaler->in.height);
    CJSON_SET_STRING(node, pixel format, pixel_format[scaler->in.pixfmt]);

    CJSON_CREATE_NODE(root, "output", node);
    if(!node)
        return;

    CJSON_SET_VALUE(node, width,         scaler->out.width);
    CJSON_SET_VALUE(node, height,        scaler->out.height);
    CJSON_SET_STRING(node, pixel format, pixel_format[scaler->out.pixfmt]);
}

static void cfg_load_mosaic_param(cJSON *root, mosaic_config_t *mosaic)
{
    CJSON_LOAD_STRING_TO_VALUE(root, mode, mosaic->mode, mosaic_mode);
    CJSON_LOAD_VALUE(root, posx,   mosaic->posx);
    CJSON_LOAD_VALUE(root, posy,   mosaic->posy);
    CJSON_LOAD_VALUE(root, width,  mosaic->width);
    CJSON_LOAD_VALUE(root, height, mosaic->height);
}

static void cfg_set_mosaic_param(cJSON *root, mosaic_config_t *mosaic)
{
    CJSON_SET_STRING(root, mode,  mosaic_mode[mosaic->mode]);
    CJSON_SET_VALUE(root, posx,   mosaic->posx);
    CJSON_SET_VALUE(root, posy,   mosaic->posy);
    CJSON_SET_VALUE(root, width,  mosaic->width);
    CJSON_SET_VALUE(root, height, mosaic->height);
}

static void cfg_load_debug_param(cJSON *root, debug_config_t *dcfg)
{
    cJSON *node;
    
    node = cJSON_GetObjectItem(root, "debug");
    if(node){
        CJSON_LOAD_VALUE(node, width,    dcfg->width);
        CJSON_LOAD_VALUE(node, height,   dcfg->height);
        CJSON_LOAD_STRING(node, path,    dcfg->path);
    }
}

static void cfg_set_debug_param(cJSON *root, debug_config_t *dcfg)
{
    cJSON *node;

    CJSON_CREATE_NODE(root, "debug", node);
    if(node){
        CJSON_SET_VALUE(node, width,    dcfg->width);
        CJSON_SET_VALUE(node, height,   dcfg->height);
        CJSON_SET_STRING(node, path,    dcfg->path);
    }
}

static void cfg_load_channel_obj(cJSON *root, char *obj, config_info_t *gcfg)
{
    cJSON *node;
    video_config_t *vcfg;

    node = cJSON_GetObjectItem(root, obj);
    if(!node)
        return;

    cJSON *array, *item;
    array = cJSON_GetObjectItem(node, "channels");
    if(!array)
        return;

    int count = cJSON_GetArraySize(node);
    int i, channel = 0; 

    count = (count > gcfg->max_channel) ? gcfg->max_channel : count;
    for(i = 0; i < count; i++){
        item = cJSON_GetArrayItem(array, i);
        if(!item)
            continue;

        CJSON_LOAD_VALUE(item, channel, channel);
        if(channel >= MAX_VIDEO_CHANNEL){
            continue;
        }
        vcfg = &gcfg->vcfg[channel];
        vcfg->channel = channel;

        if(strcmp(obj, "encoder") == 0){
            cfg_load_encoder_param(item, &vcfg->encode_config);
        }else if(strcmp(obj, "video input") == 0){
            cfg_load_video_in_param(item, &vcfg->input_config);
        }else if(strcmp(obj, "video output") == 0){
            cfg_load_video_out_param(item, &vcfg->output_config);
        }else if(strcmp(obj, "scaler") == 0){
            cfg_load_scaler_param(item, &vcfg->scaler_config);
        }else if(strcmp(obj, "mosaic") == 0){
            cfg_load_mosaic_param(item, &vcfg->mosaic_config);
        }else if(strcmp(obj, "debug") == 0){
            cfg_load_debug_param(item, &gcfg->dcfg[channel]);
        }else{
            
        }
    }
}

static void cfg_set_channel_obj(cJSON *root, char *obj, config_info_t *gcfg)
{
    cJSON *node, *array;
    video_config_t *vcfg;

    CJSON_CREATE_NODE(root, obj, node);

    CJSON_CREATE_ARRAY(node, "channels", array);
    if(!array){
        return;
    }
    int count;
    int i, channel; 
    cJSON * item;

    count = gcfg->max_channel;

    for(i = 0; i < count; i++){
        CJSON_CREATE_ARRAY_ITEM(array, i, item);
        if(!item){
            continue;
        }
        
        channel = i;
        CJSON_SET_VALUE(item, channel, channel);
        if(channel >= MAX_VIDEO_CHANNEL){
            continue;
        }
        vcfg = &gcfg->vcfg[channel];

        if(strcmp(obj, "encoder") == 0){
            cfg_set_encoder_param(item, &vcfg->encode_config);
        }else if(strcmp(obj, "video input") == 0){
            cfg_set_video_in_param(item, &vcfg->input_config);
        }else if(strcmp(obj, "video output") == 0){
            cfg_set_video_out_param(item, &vcfg->output_config);
        }else if(strcmp(obj, "scaler") == 0){
            cfg_set_scaler_param(item, &vcfg->scaler_config);
        }else if(strcmp(obj, "mosaic") == 0){
            cfg_set_mosaic_param(item, &vcfg->mosaic_config);
        }else if(strcmp(obj, "debug") == 0){
            cfg_set_debug_param(item, &gcfg->dcfg[channel]);
        }else{

        }
    }
}

static void cfg_adjust_param(config_info_t *info)
{
    if(info->video_mode == VIDEO_4X1080P){
        info->max_channel = 4;
        for(int i = 0; i < info->max_channel; i++){
            info->vcfg[i].channel = i;
            // info->vcfg[i].encode_config.width = 1920;
            // info->vcfg[i].encode_config.height = 1088;
            // info->vcfg[i].input_config.width = 1920;
            // info->vcfg[i].input_config.height = 1080;
            // info->vcfg[i].output_config.width = 1920;
            // info->vcfg[i].output_config.height = 1080;
            // info->vcfg[i].scaler_config.in.width = 1920;
            // info->vcfg[i].scaler_config.in.height = 1080;
            // info->dcfg[i].width = 1920;
            // info->dcfg[i].height = 1080;
            info->vcfg[i].encode_config.gop.length = info->vcfg[i].encode_config.rc.framerate;
            info->vcfg[i].encode_config.gop.freqidr = info->vcfg[i].encode_config.rc.framerate;
        }
    }
    if(info->video_mode == VIDEO_1X2160P){
        info->max_channel = 1;

        info->vcfg[0].channel = 0;
        // info->vcfg[0].encode_config.width = 3840;
        // info->vcfg[0].encode_config.height = 2168;
        // info->vcfg[0].input_config.width = 3840;
        // info->vcfg[0].input_config.height = 2160;
        // info->vcfg[0].output_config.width = 3840;
        // info->vcfg[0].output_config.height = 2160;
        // info->vcfg[0].scaler_config.in.width = 3840;
        // info->vcfg[0].scaler_config.in.height = 2160;
        // info->dcfg[0].width = 3840;
        // info->dcfg[0].height = 2160;
        info->vcfg[0].encode_config.gop.length = info->vcfg[0].encode_config.rc.framerate;
        info->vcfg[0].encode_config.gop.freqidr = info->vcfg[0].encode_config.rc.framerate;
    }
}

static int32_t cfg_load_config_file(cfg_t *config)
{
    int fd;

    if(!config->input_config_file){
        DBG(DBG_INFO, "No input config file, use default config file\n");
        config->input_config_file = config->default_config_file;
    }

    fd = open(config->input_config_file, O_RDONLY, 0655);
    if(fd > 0){
        char *buffer;
        int size = 0x80000;
        
        buffer = malloc(size);
        if(!buffer){
            return -ENOMEM;
        }
        size = read(fd, buffer, size);

        cJSON *root;

        root = cJSON_Parse(buffer);
        if (!root) {
            DBG(DBG_INFO, "Error before: [%s]\n",cJSON_GetErrorPtr());

            return -EINVAL;
        }

        CJSON_LOAD_STRING_TO_VALUE(root, mode,      config->info->video_mode, video_mode);
        if(config->info->video_mode == VIDEO_4X1080P){
            config->info->max_channel = 4;
        }
        if(config->info->video_mode == VIDEO_1X2160P){
            config->info->max_channel = 1;
        }
        /*load encode configure*/
        cfg_load_channel_obj(root, "encoder", config->info);
        /*load video input parameter*/
        cfg_load_channel_obj(root, "video input", config->info);
        /*load video output parameter*/
        cfg_load_channel_obj(root, "video output", config->info);
        /*load scaler parameter*/
        cfg_load_channel_obj(root, "scaler", config->info);
        /*load mosaic parameter*/
        cfg_load_channel_obj(root, "mosaic", config->info);
        /*load debug parameter*/
        cfg_load_channel_obj(root, "debug", config->info);

        cfg_adjust_param(config->info);
            
        cJSON_Delete(root);

        close(fd);
        free(buffer);
        buffer = NULL;
    }else{
        DBG(DBG_INFO, "can't open default config file, create default\n");
    }

    config->op->save_config(config);

    return 0;
}

static int32_t cfg_save_config_file(cfg_t *config)
{
    cJSON *root;
    int32_t ret;

    root = cfg_json_create();
    if(!root)
        return -ENOMEM;

    CJSON_SET_STRING(root, mode,    video_mode[config->info->video_mode]);

    cfg_adjust_param(config->info);
    
    /*set encoder parameter*/
    cfg_set_channel_obj(root, "encoder", config->info);
    /*set video input parameter*/
    cfg_set_channel_obj(root, "video input", config->info);
    /*set video output parameter*/
    cfg_set_channel_obj(root, "video output", config->info);
    /*set scaler parameter*/
    cfg_set_channel_obj(root, "scaler", config->info);
    /*set mosaic parameter*/
    cfg_set_channel_obj(root, "mosaic", config->info);
    /*set debug parameter*/
    cfg_set_channel_obj(root, "debug", config->info);
    
    ret = cfg_json_save(config, root);
    if(ret != 0)
        return ret;

    return 0;
}

static int32_t cfg_init(cfg_t *config)
{
    config->default_config_file = "encoder.json";
    config->info = &g_config;

    return 0;
}

static int32_t cfg_release(cfg_t *config)
{
    config->default_config_file = NULL;
    config->info = NULL;

    return 0;
}

cfg_operation_t cfg_operation = {
    .init = cfg_init,
    .release = cfg_release,
    
    .load_config = cfg_load_config_file,
    .save_config = cfg_save_config_file,
};

int32_t config_create(cfg_t **config)
{
    cfg_t* pcfg;

    (*config) = (cfg_t*)malloc(sizeof(cfg_t));
    if(!(*config)){
        DBG(DBG_ERR,"malloc config error\n");
        return -ENOMEM;
    }

    pcfg = *config;
    memset(pcfg,0,sizeof(cfg_t));

    pcfg->op = &cfg_operation;
    pcfg->op->init(pcfg);

    return 0;
}

void config_destroy(cfg_t *config)
{
    if(config){
        config->op->release(config);
        free(config);
        config = NULL;
    }
}

config_info_t *get_config_info(void)
{
    return &g_config;
}